package edu.nf.db.util;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ServiceLoader;

/**
 * @author zhu
 * @date 2024-05-08
 */
public class BeanProcessor {

    /**
     * 基于SPI加载所有TypeHandler实现
     */
    private static final ServiceLoader<TypeConvertor> TYPE_CONVERTER_LOADER = ServiceLoader.load(TypeConvertor.class);

    /**
     * 根据结果集创建 Bean 实例
     * @param rs 结果集
     * @param beanClass 转换的 bean 实体类
     * @return 返回 Bean 实例
     * @param <T> bean 实体类型
     * @throws SQLException 运行时产生的SQL异常
     */
    public static <T> T createBean(ResultSet rs, Class<T> beanClass) throws SQLException {
        // 实例化 beanClass 对象
        T bean = newInstance(beanClass);
        // 获取 bean 的属性描述器
        PropertyDescriptor[] pds = propertyDescriptors(beanClass);
        // 循环遍历结果集MetaData的columnCount
        for (int i = 1; i <= rs.getMetaData().getColumnCount(); i++) {
            // 获取列名
            String columnLabel = rs.getMetaData().getColumnLabel(i);
            // 循环所有的PropertyDescriptor（属性描述符）
            for (PropertyDescriptor pd : pds) {
                // 解析PropertyDescriptor是否匹配映射的字段，如果匹配则执行映射
                if (hasColumnLabel(columnLabel, pd, beanClass)){
                    // 通过 ResultSet 获取 value
                    Object value = rs.getObject(columnLabel);

                    if (value != null) {
                        for(TypeConvertor convertor : TYPE_CONVERTER_LOADER){
                            if (convertor.supports(pd.getPropertyType())){
                                value = convertor.convert(pd.getPropertyType(),value);
                                break;
                            }
                        }
                    }
                    // 注意：当 value 是 null 同时字段的类型是基本类型时，赋值会产生异常
                    // 所以需要处理这种情况
                    if (pd.getPropertyType().isPrimitive() && value == null) {
                        break;
                    }
                    // 赋值操作
                    processColumn(value, pd, bean);
                    break;
                }
            }
        }
        return bean;
    }

    /**
     * 将 bean 实体类进行实例化
     *
     * @param beanClass bean实体类型
     * @return bean实体类型的实体对象
     * @param <T> bean 实体类型
     */
     public static <T> T newInstance(Class<T> beanClass) {
         try {
             return beanClass.getConstructor().newInstance();
         } catch (Exception e) {
             throw new RuntimeException("Cannot create " + beanClass.getName() + ": " + e.getMessage());
         }
     }

    /**
     * 解析属性描述器
     *
     * @param beanClass bean实体类型
     * @return bean实体类的属性描述器
     */
     private static PropertyDescriptor[] propertyDescriptors(Class<?> beanClass) {
         try {
             BeanInfo beanInfo = Introspector.getBeanInfo(beanClass, Object.class);
             return beanInfo.getPropertyDescriptors();
         } catch (IntrospectionException e) {
             throw new RuntimeException(e);
         }
     }

    /**
     * 根据属性描述器解析是否存在此列名
     *
     * @param columnLabel 要进行解析的列
     * @param pd 属性描述器
     * @param beanClass bean的Class对象
     * @return true表示存在此列，false则不存在
     * @throws SQLException sql异常
     */
     private static boolean hasColumnLabel(String columnLabel, PropertyDescriptor pd, Class<?> beanClass) throws SQLException {

         try {
             // 属性字段名
             String fieldName = pd.getName();
             // 获取Field字段实例
             Field field = beanClass.getDeclaredField(fieldName);
             // 获取字段上的注解
             if (field.isAnnotationPresent(Column.class)) {
                 fieldName = field.getAnnotation(Column.class).value();
             }
             // 比较列名和注解
             return fieldName.equalsIgnoreCase(columnLabel);
         } catch (NoSuchFieldException e) {
             throw new SQLException("Cannot resolve " + pd.getName() + ": " + e.getMessage());
         }
     }

    /**
     * 调用描述器的set方法进行赋值操作
     *
     * @param value 赋值参数
     * @param pd 属性描述器
     * @param beanInstance bean的实例
     * @throws SQLException sql异常
     */
     private static void processColumn(Object value, PropertyDescriptor pd, Object beanInstance) throws SQLException {
         try {
             // 将 value 赋值给 field，获取set方法进行赋值
             pd.getWriteMethod().invoke(beanInstance, value);
         } catch (Exception e) {
             throw new SQLException(e);
         }
     }
}
